﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using HgCo.WindowsLive.SkyDrive.TotalCommander.Plugins.Forms;
using Tools.TotalCommanderT;

// TODO: I find that an exception is coming from C++: Remoting loses connection to SkyDriveWfx instance after a while.
// TODO: LogOn information should come from a config file (password should be encrypted)
// TODO: Implement copy/move files and folders directly on SkyDrive.

namespace HgCo.WindowsLive.SkyDrive.TotalCommander.Plugins
{
    /// <summary>
    /// Represents a Total Commander File System Plugin (.wfx) for Windows Live SkyDrive.
    /// </summary>
    [TotalCommanderPlugin("SkyDriveExplorer")]
    [ResourcePluginIcon(typeof(SkyDriveWfx), "HgCo.WindowsLive.SkyDrive.TotalCommander.Plugins.Resources.SkyDriveWfx", "mini_storage")]
    public class SkyDriveWfx : FileSystemPlugin
    {
        #region Fields

        /// <summary>
        /// The timeout for SkyDrive WebClient.
        /// </summary>
        private const int SkyDriveWebClientTimeout = 5 * 1000;

        /// <summary>
        /// The path URL of the root webfolderitem.
        /// </summary>
        private static readonly string RootWebFolderItemPathUrl = WebFolderItemInfo.PathUrlSegmentDelimiter.ToString();

        /// <summary>
        /// The dictionary used to cache webfolderitems by their parent's path URL.
        /// </summary>
        private readonly Dictionary<string, WebFolderItemInfo[]> dicWebFolderItem;

        /// <summary>
        /// The path URL of the webfolder listed last time.
        /// </summary>
        private string lastListedFolderPathUrl;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the name of SkyDriveWfx displayed in Total Commander.
        /// </summary>
        /// <value>The name of SkyDriveWfx.</value>
        public override string Name
        {
            get { return "SkyDrive Explorer"; }
        }

        /// <summary>
        /// Gets or sets the session for SkyDriveWebClient.
        /// </summary>
        /// <value>The session for SkyDriveWebClient.</value>
        private WebSession Session { get; set; }

        #endregion

        #region Constructors
        
        /// <summary>
        /// Initializes a new instance of the <see cref="SkyDriveWfx"/> class.
        /// </summary>
        public SkyDriveWfx()
        {
            dicWebFolderItem = new Dictionary<string, WebFolderItemInfo[]>();
            lastListedFolderPathUrl = String.Empty;
        }

        #endregion

        #region Public Methods

        #region Pre- and PostOperation Related Methods

        /// <summary>
        /// OnOperationStarting is called just as an information to the plugin that a certain operation starts.
        /// </summary>
        /// <param name="e">The <see cref="Tools.TotalCommanderT.OperationEventArgs"/> instance containing the event data.</param>
        public override void OnOperationStarting(OperationEventArgs e)
        {
            base.OnOperationStarting(e);
        }

        /// <summary>
        /// OnOperationStatusChanged is called just as an information to the plugin that a certain operation starts or ends.
        /// </summary>
        /// <param name="e">The <see cref="Tools.TotalCommanderT.OperationEventArgs"/> instance containing the event data.</param>
        public override void OnOperationStatusChanged(OperationEventArgs e)
        {
            base.OnOperationStatusChanged(e);
        }

        /// <summary>
        /// OnOperationStarting is called just as an information to the plugin that a certain operation ends.
        /// </summary>
        /// <param name="e">The <see cref="Tools.TotalCommanderT.OperationEventArgs"/> instance containing the event data.</param>
        public override void OnOperationFinished(OperationEventArgs e)
        {
            base.OnOperationFinished(e);
        }

        #endregion

        #region Operation Connection Related Methods

        ///// <summary>
        ///// Disconnects from SkyDrive.
        ///// </summary>
        ///// <param name="DisconnectRoot">The disconnect root.</param>
        ///// <returns><c>true</c> if disconnection was successful; otherwise <c>false</c>.</returns>
        //public override bool Disconnect(string DisconnectRoot)
        //{
        //    Session = null;
        //    return true;
        //}

        #endregion

        #region Operation List Related Methods
        
        /// <summary>
        /// FindFirst is called to retrieve the first webfolderitem in a webfolder of SkyDrive's file system.
        /// </summary>
        /// <param name="Path">The full path to the webfolder of which items are to be listed.</param>
        /// <param name="FindData">The FindData structure used to retrieve webfolderitem info.</param>
        /// <returns>An enumerator for the list of webfolderitems.</returns>
        public override object FindFirst(string Path, ref FindData FindData)
        {
            string folderPathUrlParent = ConvertLocalPathToPathUrl(Path);
            WebFolderItemInfo[] webFolderItems = null;
            if (!folderPathUrlParent.Equals(lastListedFolderPathUrl) &&
                dicWebFolderItem.ContainsKey(folderPathUrlParent))
            {
                webFolderItems = dicWebFolderItem[folderPathUrlParent];
            }
            else
            {
                try
                {
                    SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                    if (wcSkyDrive != null)
                    {
                        if (IsRootPathUrl(folderPathUrlParent))
                        {
                            webFolderItems = wcSkyDrive.ListRootWebFolderItems();
                        }
                        else
                        {
                            WebFolderInfo webFolderRoot = FindRootWebFolder(folderPathUrlParent);
                            WebFolderInfo webFolderParent = GetWebFolder(
                                folderPathUrlParent,
                                webFolderRoot);
                            webFolderItems = wcSkyDrive.ListSubWebFolderItems(webFolderParent);
                        }
                        dicWebFolderItem[folderPathUrlParent] = webFolderItems;
                    }
                }
                catch (Exception ex) 
                {
                    SkyDriveWfxErrorForm formError = new SkyDriveWfxErrorForm(ex);
                    formError.ShowDialog();
                }
            }

            lastListedFolderPathUrl = folderPathUrlParent;

            if (webFolderItems != null)
            {
                IEnumerator enumeratorWebFolderItem = webFolderItems.GetEnumerator();
                if (enumeratorWebFolderItem.MoveNext())
                {
                    FindData = FillFindData(enumeratorWebFolderItem.Current as WebFolderItemInfo);
                    return enumeratorWebFolderItem;
                }
            }
            return null;
        }

        /// <summary>
        /// FindNext is called to retrieve the next webfolderitem in a webfolder of SkyDrive's file system.
        /// </summary>
        /// <param name="Status">The enumerator returned by FindFirst method.</param>
        /// <param name="FindData">The FindData structure used to retreive webfolderitem info.</param>
        /// <returns><c>true</c> if there are more webfolderitems to return, otherwise <c>false</c>.</returns>
        public override bool FindNext(object Status, ref FindData FindData)
        {
            bool hasMore = false;
            IEnumerator enumeratorWebFolderItem = Status as IEnumerator;
            if (enumeratorWebFolderItem != null)
            {
                if (enumeratorWebFolderItem.MoveNext())
                {
                    FindData = FillFindData(enumeratorWebFolderItem.Current as WebFolderItemInfo);
                    hasMore = true;
                }
            }
            return hasMore;
        }

        /// <summary>
        /// FindClose is called to end a FindFirst/FindNext loop, 
        /// either after retrieving all files, or when the user aborts it.
        /// </summary>
        /// <param name="Status">The enumerator returned by FindFirst method.</param>
        public override void FindClose(object Status)
        {
            base.FindClose(Status);

            IDisposable disposable = Status as IDisposable;
            if (disposable != null)
                disposable.Dispose();
        }

        /// <summary>
        /// ExctractCustomIcon is called when a webfolderitem is displayed in the file list. 
        /// It can be used to specify a custom icon for that webfolderitem.
        /// </summary>
        /// <param name="RemoteName">The full name of the webfolderitem to be displayed.</param>
        /// <param name="ExtractFlags">Can be combination of the <see cref="IconExtractFlags"/> values.</param>
        /// <param name="TheIcon">The webfolderitem icon.</param>
        /// <returns>One of <see cref="IconExtractResult"/> values.</returns>
        public override IconExtractResult ExctractCustomIcon(ref string RemoteName, IconExtractFlags ExtractFlags, ref System.Drawing.Icon TheIcon)
        {
            string pathUrl = ConvertLocalPathToPathUrl(RemoteName.EndsWith("\\", StringComparison.CurrentCultureIgnoreCase) ?
                RemoteName.Substring(0, RemoteName.Length - 1) : RemoteName);
            WebFolderItemInfo webFolderItem = FindWebFolderItem(pathUrl);

            if (webFolderItem != null)
                try
                {
                    SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                    System.Drawing.Image imgIcon = wcSkyDrive.DownloadWebFolderItemIcon(webFolderItem.WebIcon);
                    System.Drawing.Bitmap bmpIcon = imgIcon as System.Drawing.Bitmap;
                    if (bmpIcon != null)
                    {
                        IntPtr handleIcon = bmpIcon.GetHicon();
                        TheIcon = System.Drawing.Icon.FromHandle(handleIcon);
                        return IconExtractResult.Extracted;

                    }
                    else return IconExtractResult.UseDefault;
                }
                catch
                {
                    return IconExtractResult.UseDefault;
                }
            else
                return IconExtractResult.UseDefault;
        }

        /// <summary>
        /// GetPreviewBitmap is called when a webfolderitem is displayed in thumbnail view. 
        ///  It can be used to return a custom bitmap for that webfolderitem.
        /// </summary>
        /// <param name="RemoteName">The full name of the webfolderitem to be displayed.</param>
        /// <param name="width">The maximum width of the preview bitmap.</param>
        /// <param name="height">The maximum height of the preview bitmap.</param>
        /// <returns>The preview bitmap.</returns>
        public override BitmapResult GetPreviewBitmap(string RemoteName, int width, int height)
        {
            BitmapResult bmpResult = null;
            string pathUrl = ConvertLocalPathToPathUrl(RemoteName);

            try
            {
                SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                WebFolderItemInfo webFolderItem = FindWebFolderItem(pathUrl);
                WebFolderInfo webFolderRoot = FindRootWebFolder(pathUrl);

                if (wcSkyDrive != null && webFolderItem != null)
                {
                    if (webFolderItem.ItemType == WebFolderItemType.Folder)
                    {
                        webFolderItem = GetWebFolder(webFolderItem.PathUrl, webFolderRoot);
                    }
                    else
                    {
                        string folderPathUrlParent = ConvertLocalPathToPathUrl(System.IO.Path.GetDirectoryName(RemoteName));
                        WebFolderInfo webFolderParent = GetWebFolder(folderPathUrlParent, webFolderRoot);
                        webFolderItem = GetWebFile(pathUrl, webFolderParent);
                    }

                    wcSkyDrive.GetWebFolderItem(webFolderItem);
                    System.Drawing.Image imgWebFolder = wcSkyDrive.DownloadWebFolderItemIcon(webFolderItem.WebIcon);

                    System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(width, height);
                    using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp))
                    {
                        g.Clear(System.Drawing.Color.White);
                        g.DrawImage(
                            imgWebFolder,
                            0, 0,
                            Math.Min(width, imgWebFolder.Width),
                            Math.Min(height, imgWebFolder.Height));
                    }
                    bmpResult = new BitmapResult(bmp);
                    bmpResult.Cache = true;
                }
            }
            catch
            {
            }
            return bmpResult;
        }

        #endregion

        #region Operation Directory Related Methods

        /// <summary>
        /// MkDir is called to create a webfolder on SkyDrive's file system.
        /// </summary>
        /// <param name="Path">The full name of the webfolder to be created.</param>
        /// <returns><c>true</c>, if the webfolder could be created; otherwise, <c>false</c>.</returns>
        public override bool MkDir(string Path)
        {
            bool success = false;
            string folderName = System.IO.Path.GetFileName(Path);
            string folderPathUrl = ConvertLocalPathToPathUrl(Path);
            string folderPathUrlParent = ConvertLocalPathToPathUrl(System.IO.Path.GetDirectoryName(Path));
            
            try
            {
                SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                if (wcSkyDrive != null)
                {
                    if (IsRootPathUrl(folderPathUrlParent))
                    {
                        SkyDriveWfxNewRootFolderForm formNewRootFolder = new SkyDriveWfxNewRootFolderForm();
                        formNewRootFolder.ShowDialog();

                        wcSkyDrive.CreateRootWebFolder(folderName, formNewRootFolder.SelectedShareType);
                        if (formNewRootFolder.SelectedContentType != WebFolderContentType.Documents)
                        {
                            WebFolderInfo webFolderNew = new WebFolderInfo
                            {
                                PathUrl = folderPathUrl,
                                ShareType = formNewRootFolder.SelectedShareType
                            };
                            wcSkyDrive.ChangeRootWebFolderContentType(webFolderNew, formNewRootFolder.SelectedContentType);
                        }
                    }
                    else
                    {
                        WebFolderInfo webFolderRoot = FindRootWebFolder(folderPathUrl);
                        WebFolderInfo webFolderParent = GetWebFolder(
                            folderPathUrlParent,
                            webFolderRoot);

                        wcSkyDrive.CreateSubWebFolder(folderName, webFolderParent);
                    }
                }

                dicWebFolderItem.Remove(folderPathUrlParent);
                success = true;
            }
            catch (Exception ex)
            {
                SkyDriveWfxErrorForm formError = new SkyDriveWfxErrorForm(ex);
                formError.ShowDialog();
            }

            return success;
        }

        /// <summary>
        /// RemoveDir is called to remove a webfolder from SkyDrive's file system.
        /// </summary>
        /// <param name="RemoteName">The full name of the webfolder to be removed.</param>
        /// <returns><c>true</c> if the webfolder could be removed; otherwise, <c>false</c>.</returns>
        public override bool RemoveDir(string RemoteName)
        {
            bool success = false;
            string folderPathUrl = ConvertLocalPathToPathUrl(RemoteName);
            string folderPathUrlParent = ConvertLocalPathToPathUrl(System.IO.Path.GetDirectoryName(RemoteName));

            try
            {
                WebFolderInfo webFolderRoot = FindRootWebFolder(folderPathUrl);
                WebFolderInfo webFolder = GetWebFolder(
                    folderPathUrl,
                    webFolderRoot);

                SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                if (wcSkyDrive != null)
                    wcSkyDrive.DeleteWebFolder(webFolder);
                
                dicWebFolderItem.Remove(folderPathUrlParent);
                success = true;
            }
            catch (Exception ex)
            {
                SkyDriveWfxErrorForm formError = new SkyDriveWfxErrorForm(ex);
                formError.ShowDialog();
            }

            return success;
        }

        #endregion

        #region Operation File Related Methods

        /// <summary>
        /// GetFile is called to transfer a webfile from SkyDrive's file system to the normal/local file system.
        /// </summary>
        /// <param name="RemoteName">The full name of the webfile to be transfered.</param>
        /// <param name="LocalName">The full name of the local file where webfile needs to be transfered.</param>
        /// <param name="CopyFlags">Can be combination of the <see cref="CopyFlags"/> values.</param>
        /// <param name="info">It contains information about the webfile which was previously retrieved via <see cref="FindFirst"/>/<see cref="FindNext"/>.</param>
        /// <returns>One of the <see cref="FileSystemExitCode"/> values.</returns>
        public override FileSystemExitCode GetFile(string RemoteName, ref string LocalName, CopyFlags CopyFlags, RemoteInfo info)
        {
            string filePathUrl = ConvertLocalPathToPathUrl(RemoteName);
            string folderPathUrlParent = ConvertLocalPathToPathUrl(System.IO.Path.GetDirectoryName(RemoteName));
            System.IO.FileInfo fiLocal = new System.IO.FileInfo(LocalName);

            if ((CopyFlags & CopyFlags.Overwrite) != CopyFlags.Overwrite && fiLocal.Exists)
                return FileSystemExitCode.FileExists;

            try
            {
                WebFolderInfo webFolderRoot = FindRootWebFolder(filePathUrl);
                WebFolderInfo webFolderParent = GetWebFolder(
                    folderPathUrlParent,
                    webFolderRoot);
                WebFileInfo webFile = GetWebFile(filePathUrl, webFolderParent);

                SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                if (wcSkyDrive != null)
                {
                    using (System.IO.Stream sr = wcSkyDrive.DownloadWebFile(webFile))
                    using (System.IO.FileStream fsw = new System.IO.FileStream(LocalName, System.IO.FileMode.OpenOrCreate))
                    {
                        byte[] buffer = new byte[64 * 1024];
                        int count = 0;
                        while ((count = sr.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            try
                            {
                                fsw.Write(buffer, 0, count);
                            }
                            catch
                            {
                                return FileSystemExitCode.WriteError;
                            }
                            int percentDone = (int)((info.Size > 0 ? (fsw.Length / (decimal)info.Size) : 0M) * 100);
                            if (ProgressProc(RemoteName, LocalName, percentDone))
                                return FileSystemExitCode.UserAbort;
                        }
                    }
                }
                return FileSystemExitCode.OK;
            }
            catch (Exception ex)
            {
                SkyDriveWfxErrorForm formError = new SkyDriveWfxErrorForm(ex);
                formError.ShowDialog();
                
                return FileSystemExitCode.ReadError;
            }
        }

        /// <summary>
        /// PutFile is called to transfer a file from the normal/local file system  to SkyDrive's file system.
        /// </summary>
        /// <param name="LocalName">The full name of the local file to be transfered.</param>
        /// <param name="RemoteName">The full name of the webfile where local file needs to be transfered.</param>
        /// <param name="CopyFlags">Can be combination of the <see cref="CopyFlags"/> values.</param>
        /// <returns>One of the <see cref="FileSystemExitCode"/> values.</returns>
        public override FileSystemExitCode PutFile(string LocalName, ref string RemoteName, CopyFlags CopyFlags)
        {
            string remoteName = RemoteName;
            string filePathUrl = ConvertLocalPathToPathUrl(remoteName);
            string folderPathUrlParent = ConvertLocalPathToPathUrl(System.IO.Path.GetDirectoryName(remoteName));

            try
            {
                WebFolderInfo webFolderRoot = FindRootWebFolder(folderPathUrlParent);
                WebFolderInfo webFolderParent = GetWebFolder(
                    folderPathUrlParent,
                    webFolderRoot);

                SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                if (wcSkyDrive != null)
                {
                    if ((CopyFlags & CopyFlags.Overwrite) != CopyFlags.Overwrite)
                    {
                        WebFileInfo webFile = GetWebFile(filePathUrl, webFolderParent);
                        if (wcSkyDrive.IsWebFileExists(webFile))
                            return FileSystemExitCode.FileExists;
                    }

                    wcSkyDrive.UploadWebFileProgressChanged += new EventHandler<UploadWebFileProgressChangedEventArgs>(delegate(object sender, UploadWebFileProgressChangedEventArgs e)
                        {
                            ProgressProc(LocalName, remoteName, e.ProgressPercentage);
                        });
                    wcSkyDrive.UploadWebFile(LocalName, webFolderParent);
                }

                dicWebFolderItem.Remove(folderPathUrlParent);
                return FileSystemExitCode.OK;
            }
            catch (Exception ex)
            {
                SkyDriveWfxErrorForm formError = new SkyDriveWfxErrorForm(ex);
                formError.ShowDialog();
                
                return FileSystemExitCode.WriteError;
            }
        }

        /// <summary>
        /// RenMovFile is called to transfer (copy or move) or rename a webfolderitem within SkyDrive's file system.
        /// </summary>
        /// <param name="OldName">The full name of the source webfolderitem.</param>
        /// <param name="NewName">The full name of the destination webfolderitem.</param>
        /// <param name="Move"><c>true</c> if webfolderitem needs to be moved to the new location and name; otherwise <c>false</c>.</param>
        /// <param name="OverWrite"><c>true</c> if target webfolderitem should be overwritten; otherwise <c>false</c>.</param>
        /// <param name="info">It contains information about the webfolderitem which was previously retrieved via <see cref="FindFirst"/>/<see cref="FindNext"/>.</param>
        /// <returns>One of the <see cref="FileSystemExitCode"/> values.</returns>
        public override FileSystemExitCode RenMovFile(string OldName, string NewName, bool Move, bool OverWrite, RemoteInfo info)
        {
            string pathUrlOld = ConvertLocalPathToPathUrl(OldName);
            string pathUrlNew = ConvertLocalPathToPathUrl(NewName);
            string folderPathUrlParentOld = ConvertLocalPathToPathUrl(System.IO.Path.GetDirectoryName(OldName));
            string folderPathUrlParentNew = ConvertLocalPathToPathUrl(System.IO.Path.GetDirectoryName(NewName));

            try
            {
                SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                WebFolderItemInfo webFolderItem = FindWebFolderItem(pathUrlOld);

                if (wcSkyDrive != null && webFolderItem != null)
                {
                    if (Move)
                    {
                        // If only the items should be renamed
                        if (folderPathUrlParentOld.Equals(folderPathUrlParentNew, StringComparison.CurrentCultureIgnoreCase))
                        {
                            string nameNew = pathUrlNew.Remove(0, pathUrlNew.LastIndexOf(WebFolderItemInfo.PathUrlSegmentDelimiter) + 1);
                            if (webFolderItem.ItemType == WebFolderItemType.File)
                                nameNew = System.IO.Path.GetFileNameWithoutExtension(nameNew);

                            wcSkyDrive.RenameWebFolderItem(webFolderItem, nameNew);
                            dicWebFolderItem.Remove(folderPathUrlParentOld);
                            
                            return FileSystemExitCode.OK;
                        }
                    }
                }
            }
            catch (Exception ex) 
            {
                SkyDriveWfxErrorForm formError = new SkyDriveWfxErrorForm(ex);
                formError.ShowDialog();
            }
            
            return FileSystemExitCode.NotSupported;
        }

        /// <summary>
        /// DeleteFile is called to delete a webfile from SkyDrive's file system.
        /// </summary>
        /// <param name="RemoteName">The full name of the webfile to be deleted.</param>
        /// <returns><c>true</c> if the webfile could be deleted; otherwise, <c>false</c>.</returns>
        public override bool DeleteFile(string RemoteName)
        {
            bool success = false;
            string filePathUrl = ConvertLocalPathToPathUrl(RemoteName);
            string folderPathUrlParent = ConvertLocalPathToPathUrl(System.IO.Path.GetDirectoryName(RemoteName));

            try
            {
                WebFolderInfo webFolderRoot = FindRootWebFolder(filePathUrl);
                WebFolderInfo webFolderParent = GetWebFolder(
                    folderPathUrlParent,
                    webFolderRoot);
                WebFileInfo webFile = GetWebFile(filePathUrl, webFolderParent);

                SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                if (wcSkyDrive != null)
                    wcSkyDrive.DeleteWebFile(webFile);

                dicWebFolderItem.Remove(folderPathUrlParent);
                success = true;
            }
            catch (Exception ex)
            {
                SkyDriveWfxErrorForm formError = new SkyDriveWfxErrorForm(ex);
                formError.ShowDialog();
            }

            return success;
        }

        /// <summary>
        /// ExecuteFile is called to execute a command on a webfolderitem on the SkyDrive's file system. 
        /// It is also called to show a SkyDrive configuration dialog when the user right clicks on the plugin root and chooses 'properties'.
        /// </summary>
        /// <param name="hMainWin">The parent window's handler.</param>
        /// <param name="RemoteName">The full name of the webfolderitem to be executed/show its properties.</param>
        /// <param name="Verb">The command to execute on the webfolderitem: open, properties, chmod, or quote.</param>
        /// <returns>One of <see cref="ExecExitCode"/> values.</returns>
        public override ExecExitCode ExecuteFile(IntPtr hMainWin, ref string RemoteName, string Verb)
        {
            return base.ExecuteFile(hMainWin, ref RemoteName, Verb);
        }

        /// <summary>
        /// ShowFileInfo is called to show a webfolderitem properties.
        /// </summary>
        /// <param name="hMainWin">The handle to parent window.</param>
        /// <param name="RemoteName">The full name of the webfolderitem to be showed.</param>
        /// <returns>One of <see cref="ExecExitCode"/> values.</returns>
        protected override ExecExitCode ShowFileInfo(IntPtr hMainWin, string RemoteName)
        {
            string pathUrl = ConvertLocalPathToPathUrl(RemoteName);

            if (IsRootPathUrl(pathUrl))
                Session = LogOn();
            else
                try
                {
                    SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                    if (wcSkyDrive != null)
                    {
                        WebFolderItemInfo webFolderItem = FindWebFolderItem(pathUrl);
                        webFolderItem = wcSkyDrive.GetWebFolderItem(webFolderItem);
                        System.Drawing.Image webFolderItemIcon = wcSkyDrive.DownloadWebFolderItemIcon(webFolderItem.WebIcon);
                        if (webFolderItem.ItemType == WebFolderItemType.Folder)
                        {
                            SkyDriveWfxFolderInfoForm formFolderInfo = new SkyDriveWfxFolderInfoForm((WebFolderInfo)webFolderItem, webFolderItemIcon);
                            formFolderInfo.Show();
                        }
                        else if (webFolderItem.ItemType == WebFolderItemType.File)
                        {
                            SkyDriveWfxFileInfoForm formFileInfo = new SkyDriveWfxFileInfoForm((WebFileInfo)webFolderItem, webFolderItemIcon);
                            formFileInfo.Show();
                        }
                    }
                }
                catch (Exception ex)
                {
                    SkyDriveWfxErrorForm formError = new SkyDriveWfxErrorForm(ex);
                    formError.ShowDialog();

                    return ExecExitCode.Error;
                }

            return ExecExitCode.OK;
        }

        #endregion
        
        #endregion

        #region Private Methods

        /// <summary>
        /// Logs on to SkyDrive.
        /// </summary>
        /// <returns>The session if log on was successful; otherwise, null.</returns>
        private WebSession LogOn()
        {
            WebSession session = null;
            
            SkyDriveWfxLogOnForm formLogOn = new SkyDriveWfxLogOnForm();
            if (formLogOn.ShowDialog() == DialogResult.OK)
            {
                session = formLogOn.Session;
                //LogProc(LogKind.Connect, String.Format(CultureInfo.InvariantCulture, @"CONNECT \{0}", Name));
            }

            return session;
        }

        /// <summary>
        /// Gets a SkyDrive WebClient (logs on if required).
        /// </summary>
        /// <returns>The SkyDrive WebClient.</returns>
        private SkyDriveWebClient GetSkyDriveWebClient()
        {
            SkyDriveWebClient wcSkyDrive = null;

            if (Session == null)
                Session = LogOn();

            if (Session != null)
            {
                wcSkyDrive = new SkyDriveWebClient(Session);
                wcSkyDrive.Timeout = SkyDriveWebClientTimeout;
            }
            
            return wcSkyDrive;
        }

        /// <summary>
        /// Converts the normal/local path to SkyDrive's path URL.
        /// </summary>
        /// <param name="pathLocal">The normal/local path to convert.</param>
        /// <returns>The SkyDrive's path URL.</returns>
        private string ConvertLocalPathToPathUrl(string pathLocal)
        {
            string pathUrl = !String.IsNullOrEmpty(pathLocal) ? 
                pathLocal.Replace('\\', WebFolderItemInfo.PathUrlSegmentDelimiter) : 
                WebFolderItemInfo.PathUrlSegmentDelimiter.ToString();
            return pathUrl;
        }

        /// <summary>
        /// Determines whether the specified path URL is a root one.
        /// </summary>
        /// <param name="pathUrl">The path URL.</param>
        /// <returns><c>true</c> if the specified path URL is root one; otherwise, <c>false</c>.</returns>
        private bool IsRootPathUrl(string pathUrl)
        {
            return (String.IsNullOrEmpty(pathUrl) ||
                pathUrl.Equals(WebFolderItemInfo.PathUrlSegmentDelimiter.ToString(), StringComparison.InvariantCultureIgnoreCase));
        }

        /// <summary>
        /// Gets the webfolders in SkyDrive's root.
        /// </summary>
        /// <returns>The list of webfolders in SkyDrive's root.</returns>
        private WebFolderInfo[] GetRootWebFolders()
        {
            WebFolderInfo[] rootWebFolders = null;
            WebFolderItemInfo[] rootWebFolderItems =
                dicWebFolderItem.ContainsKey(RootWebFolderItemPathUrl) ?
                dicWebFolderItem[RootWebFolderItemPathUrl] : null;

            if (rootWebFolderItems != null)
            {
                List<WebFolderInfo> lWebFolderRoot = new List<WebFolderInfo>(rootWebFolderItems.Length);
                for (int idx = 0; idx < rootWebFolderItems.Length; idx++)
                    if (rootWebFolderItems[idx].ItemType == WebFolderItemType.Folder)
                        lWebFolderRoot.Add((WebFolderInfo)rootWebFolderItems[idx]);
                rootWebFolders = lWebFolderRoot.ToArray();
            }
            else
            {
                SkyDriveWebClient wcSkyDrive = GetSkyDriveWebClient();
                if (wcSkyDrive != null)
                    rootWebFolders = wcSkyDrive.ListRootWebFolders();
                else rootWebFolders = new WebFolderInfo[0];
                dicWebFolderItem[RootWebFolderItemPathUrl] = rootWebFolders;
            }

            return rootWebFolders;
        }

        /// <summary>
        /// Finds a root webfolder specified by the path URL.
        /// </summary>
        /// <param name="pathUrl">The path URL.</param>
        /// <returns>The root webfolder if it's found; otherwise, null.</returns>
        private WebFolderInfo FindRootWebFolder(string pathUrl)
        {
            if (!String.IsNullOrEmpty(pathUrl))
            {
                WebFolderInfo[] webFolderRoots = GetRootWebFolders();
                foreach (WebFolderItemInfo webFolderRoot in webFolderRoots)
                    if (pathUrl.StartsWith(webFolderRoot.PathUrl, StringComparison.InvariantCultureIgnoreCase))
                        return webFolderRoot as WebFolderInfo;
            }
            return null;
        }

        /// <summary>
        /// Finds the webfolderitem specified by the path URL.
        /// </summary>
        /// <param name="pathUrl">The path URL.</param>
        /// <returns>The webfolderitem if it's founded; otherwise, null.</returns>
        private WebFolderItemInfo FindWebFolderItem(string pathUrl)
        {
            if (!String.IsNullOrEmpty(pathUrl))
            {
                foreach (string key in dicWebFolderItem.Keys)
                {
                    WebFolderItemInfo[] webFolderItems = dicWebFolderItem[key];
                    foreach (WebFolderItemInfo webFolderItem in webFolderItems)
                        if (pathUrl.Equals(webFolderItem.PathUrl, StringComparison.InvariantCultureIgnoreCase))
                            return webFolderItem;
                }
            }
            return null;
        }

        /// <summary>
        /// Gets a webfolder object.
        /// </summary>
        /// <param name="pathUrl">The path URL.</param>
        /// <param name="webFolderRoot">The root webfolder.</param>
        /// <returns>The webfolder object.</returns>
        private WebFolderInfo GetWebFolder(string pathUrl, WebFolderInfo webFolderRoot)
        {
            WebFolderInfo webFolder = null;
            if (webFolderRoot != null)
                webFolder = new WebFolderInfo
                {
                    ContentType = webFolderRoot.ContentType,
                    ShareType = webFolderRoot.ShareType,
                    PathUrl = pathUrl
                };
            return webFolder;
        }

        /// <summary>
        /// Gets a webfile object.
        /// </summary>
        /// <param name="pathUrl">The path URL.</param>
        /// <param name="webFolderParent">The parent webfolder.</param>
        /// <returns>The webfile object.</returns>
        private WebFileInfo GetWebFile(string pathUrl, WebFolderInfo webFolderParent)
        {
            WebFileInfo webFile = new WebFileInfo
            {
                PathUrl = pathUrl,
                ShareType = webFolderParent != null ? webFolderParent.ShareType : WebFolderItemShareType.Private
            };
            return webFile;
        }

        /// <summary>
        /// Fills a FindData structure with the given webfolderitem.
        /// </summary>
        /// <param name="webFolderItem">The webfolderitem.</param>
        /// <returns>The filled FindData structure.</returns>
        private FindData FillFindData(WebFolderItemInfo webFolderItem)
        {
            FindData fd = new FindData();
            if (webFolderItem != null)
            {
                fd.Attributes = webFolderItem.ItemType == WebFolderItemType.Folder ?
                    FileAttributes.Directory : FileAttributes.Normal;
                fd.FileName = webFolderItem.PathUrl.Remove(0, webFolderItem.PathUrl.LastIndexOf('/') + 1);
                fd.AccessTime = webFolderItem.DateModified;
                fd.SetFileSize(webFolderItem.SizeMean ?? 0);
            }
            return fd;
        }

        #endregion
    }
}
